using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

using lucere.exception;
using lucere.io;

namespace lucere.index
{
    /// <summary>
    /// A collection of segmentInfo objects with methods for operating 
    /// on those segments in relation to the file system.
    /// </summary>
    public interface ISegmentInfos : IList<ISegmentInfo>, ICloneable
    {
        /// <summary>
        /// used to name new segments
        /// </summary>
        int Counter  { get; set; }

        ISegmentInfo Info(int i);

        /// <summary>
        /// Get the segments_N filename in use by this segment infos.
        /// </summary>
        string CurrentSegmentFileName { get; }

        /// <summary>
        ///  Get the next segments_N filename that will be written.
        /// </summary>
        string NextSegmentFileName { get; }

        /// <summary>
        ///    Read a particular segmentFileName.  Note that this may 
        ///    throw an IOException if a commit is in process.
        ///    
        /// </summary>
        /// <param name="directory">directory containing the segments file</param>
        /// <param name="segmentFileName">segment file to load</param>
        /// <exception cref="CorruptIndexException">if the index is corrupt</exception>
        /// <exception cref="IOException">if there is a low-level IO error</exception>
        void Read(IDirectory directory, String segmentFileName);

        /// <summary>
        /// This version of read uses the retry logic (for lock-less commits) 
        /// to find the right segments file to load.
        /// </summary>
        /// <param name="directory"></param>
        /// <exception cref="CorruptIndexException">if the index is corrupt</exception>
        /// /// <exception cref="IOException">if there is a low-level IO error</exception>
        void Read(IDirectory directory);

        /// <summary>
        /// Only non-null after prepareCommit has been called and
        /// before finishCommit is called
        /// </summary>
        IChecksumIndexOutput PendingSegnOutput { get; set; } 

        /// <summary>
        /// version number when this SegmentInfos was generated.
        /// </summary>
        long Version { get; }
        long Generation { get; }
        long LastGeneration { get; }

        /// <summary>
        /// Returns a new SegmentInfos containing the SegmentInfo instances in 
        /// the specified range first (inclusive) to last (exclusive), so total 
        /// number of segments returned is last-first.
        /// </summary>
        /// <param name="first"></param>
        /// <param name="last"></param>
        /// <returns></returns>
        ISegmentInfos Range(int first, int last);
  
        /// <summary>
        /// Carry over generation numbers from another SegmentInfos
        /// </summary>
        /// <param name="other"></param>
        void UpdateGeneration(ISegmentInfos other);
        void RollbackCommit(IDirectory dir);

        /// <summary>
        /// Call this to start a commit.  This writes the new segments file, 
        /// but writes an invalid checksum at the end, so that it is not visible 
        /// to readers.  Once this is called you must call FinishCommit to complete 
        /// the commit or RollbackCommit to abort it.
        /// </summary>
        /// <param name="dir"></param>
        void PrepareCommit(IDirectory dir);

        /// <summary>
        /// Returns all file names referenced by ISegmentInfo instances matching the provided 
        /// IDirectory (ie files associated with any "external" segments are skipped). The 
        /// returned collection is recomputed on each invocation.
        /// </summary>
        /// <param name="dir"></param>
        /// <param name="includeSegmentsFile"></param>
        IList<string> Files(IDirectory dir, bool includeSegmentsFile);

        void FinishCommit(IDirectory dir);

        /// <summary>
        /// Writes & syncs to the Directory dir, taking care to 
        /// remove the segments file on exception 
        /// </summary>
        /// <param name="dir"></param>
        void Commit(IDirectory dir);

        string SegString(IDirectory directory);

        IDictionary<string, string> UserData { get; set; }

        /// <summary>
        /// Replaces all segments in this instance, but keeps generation, 
        /// version, counter so that future commits remain write once.
        /// </summary>
        /// <param name="other"></param>
        void Replace(ISegmentInfos other);

        
        /// <summary>
        /// Used only for testing
        /// </summary>
        /// <param name="dir"></param>
        /// <returns></returns>
        bool HasExternalSegments(IDirectory dir);
}
}